/*****************************************************************************
* File Name: ir.c
*
* Version: 1.0
*
* Description:
* This file contains NEC and Samsung Protocol implementation for IR
*
* Note:
* Defining DISABLE_IR in platform.h to disable IR module
* Enable NEC_PROTOCOL macro in platform.h file to use NEC IR protocol
*
* Owner: MRAO
*
* Related Document:
* UDB Based PWM data sheet.
* TCPWM Data sheet
*
* Hardware Dependency:
* IR Transmitter Circuit.
* 
* Code Tested With:
* 1. PSoC Creator 3.1 
* 2. ARM GCC 4.8.4
******************************************************************************
* Copyright (2015), Cypress Semiconductor Corporation.
******************************************************************************
* This software is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and
* foreign), United States copyright laws and international treaty provisions.
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the
* Cypress Source Code and derivative works for the sole purpose of creating
* custom software in support of licensee product to be used only in conjunction
* with a Cypress integrated circuit as specified in the applicable agreement.
* Any reproduction, modification, translation, compilation, or representation of
* this software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH
* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
* Cypress reserves the right to make changes without further notice to the
* materials described herein. Cypress does not assume any liability arising out
* of the application or use of any product or circuit described herein. Cypress
* does not authorize its products for use as critical components in life-support
* systems where a malfunction or failure may reasonably be expected to result in
* significant injury to the user. The inclusion of Cypress' product in a life-
* support systems application implies that the manufacturer assumes all risk of
* such use and in doing so indemnifies Cypress against all charges. Use may be
* limited by and subject to the applicable Cypress software license agreement.
*****************************************************************************/
#include "ir.h"
#include "debug.h"

#ifdef NEC_PROTOCOL /*Enable this macro to use NEC protocol in this IR module*/
#define IR_INITIAL_PULSE                            (215u) /* For 13ms pulse */
#define IR_INITIAL_STOP_PULSE                       (143u) /* For 9ms pulse */
#define IR_INITIAL_STOP_REPEAT_PULSE                (180u) /* For Repeat pulse */
#define IR_DATA_PULSE                               (7u)   /* For 560us pulse */
#else
#define IR_INITIAL_PULSE                            (143u) /* For 9ms pulse */
#define IR_INITIAL_STOP_PULSE                       (72u)  /* For 4.5ms pulse */
#define IR_DATA_PULSE                               (9u)   /* For 624us pulse */
#endif /* NEC_PROTOCOL */

#define IR_DATA_LOGIC_ZERO_PULSE                     (16u) /* For 1.125ms pulse */
#define IR_DATA_LOGIC_ONE_PULSE                      (34u) /* For 2.25ms pulse */

#define IR_DATA_MSB_POSITIION                       (BIT_31_MASK) /* MSB position high value */
#ifndef DISABLE_IR

static uint16 irAddress = 0; /* Variable to store IR address */
static uint32 irData = 0; /* Data containing IR address and command to be processed */
static uint8 bitsTransmitted = 0; /* Bits transmitted */

#ifndef NEC_PROTOCOL 
/*In case of samsung protocol, the previous command is stored and retransmitted */
static uint8 prevCommand = 0; 
#endif /* NEC_PROTOCOL */

#ifdef IR_PIN_TOGGLE
/*****************************************************************************
* Function Name: IR_Pin_Toggle()
******************************************************************************
* Summary:
* This function is a callback function Pwm_IR
*
* Parameters:
* None
*
* Return:
* None
*
* Theory:
* Toggles the IR pin depending upon the Pwm_IR interrupt source 
*
* Side Effects:
* None 
* 
* Note:
* None
*****************************************************************************/
static void IR_Pin_Toggle(void)
{
    uint8 status;
    status = Pwm_IR_GetInterruptSource();
    Pwm_IR_ClearInterrupt(Pwm_IR_INTR_MASK_TC | Pwm_IR_INTR_MASK_CC_MATCH);
    /* Check for compare value status to toggle IR LED pin */
    if(status & Pwm_IR_INTR_MASK_CC_MATCH)
    {
        IR_Led_Write(LOGIC_LOW);
    }
    else if(status & Pwm_IR_INTR_MASK_TC)
    {
        IR_Led_Write(LOGIC_HIGH);
    }
}
#endif /* IR_PIN_TOGGLE */

/*****************************************************************************
* Function Name: IR_CallBack()
******************************************************************************
* Summary:
* This function is a callback function when IR interrupt fires
*
* Parameters:
* None
*
* Return:
* None
*
* Theory:
* Stops or starts the PWM pulse depending on whether the interrupt is due to 
* comparator match or period expire
*
* Side Effects:
* irData and bitsTransmitted is updated
* 
* Note:
* None
*****************************************************************************/    
static void IR_CallBack(void)
{
    uint8 status;
    status = Pwm_IR_Timer_GetInterruptSourceMasked();
    Pwm_IR_Timer_ClearInterrupt(Pwm_IR_Timer_INTR_MASK_CC_MATCH | Pwm_IR_Timer_INTR_MASK_TC);
    /*If counter is equal to the compare value then stop the IR LED*/
    if(status == Pwm_IR_Timer_INTR_MASK_CC_MATCH)
    {
        Pwm_IR_Stop();
#ifdef IR_PIN_TOGGLE/*Alternate implementation is used UDB based PWM to toggle IR pin to process commands*/
        Pwm_IR_Interrupt_Disable();
        Pwm_IR_Interrupt_ClearPending();
        IR_Led_Write(LOGIC_HIGH);
#endif /* IR_PIN_TOGGLE */
    }
    else if((status == Pwm_IR_Timer_INTR_MASK_TC) && (bitsTransmitted != 0))
    {
        Pwm_IR_Timer_Stop();
        Pwm_IR_Timer_WriteCompare(IR_DATA_PULSE);
        Pwm_IR_Timer_WriteCounter(0);
        /* Check logical 0 and 1 to write for different time period to get correct pulse */
        if(irData & IR_DATA_MSB_POSITIION)
        {
            Pwm_IR_Timer_WritePeriod(IR_DATA_LOGIC_ONE_PULSE);
        }
        else
        {
            Pwm_IR_Timer_WritePeriod(IR_DATA_LOGIC_ZERO_PULSE);            
        }

#ifdef IR_PIN_TOGGLE 
        /* Alternate implementation is used UDB based PWM to toggle IR pin to process 
         * commands*/
        Pwm_IR_Interrupt_Enable();
#endif /* IR_PIN_TOGGLE */

        Pwm_IR_Timer_Start();
        Pwm_IR_Start();
        irData = (irData<<BIT_1_POSITION);
        bitsTransmitted--;
    }
    else
    {
        IR_Stop();
    }
}

/*****************************************************************************
* Function Name: IR_Init()
******************************************************************************
* Summary:
* This function is used to initialize IR module
*
* Parameters:
* irDeviceaddress - IR Device address  
*
* Return:
* None
*
* Theory:
* Initialize the PWM modules and store the IR address that is used for 
* the IR transmission
* 
*
* Side Effects:
* irAddress and bitsTransmitted is updated
* 
* Note:
* While using NEC protocol make sure address is given in 8 bit format,
* While using SAMSUNG protocol make sure address is given in 16 bit format.      
*
*****************************************************************************/
void IR_Init(uint16 irDeviceaddress)
{
    irAddress = irDeviceaddress;
#ifdef NEC_PROTOCOL /* Enable this macro to use NEC protocol */
    irAddress = (irAddress<<BIT_8_POSITION) | ((~irAddress) & 0xFF);
#endif /* NEC_PROTOCOL */

    Pwm_IR_Init();
    Pwm_IR_Timer_Init();
    Pwm_IR_Timer_Interrupt_StartEx(IR_CallBack);
    Pwm_IR_Timer_Interrupt_Disable();
    Pwm_IR_Timer_Clk_Stop();
    Pwm_IR_Clk_Stop();

#ifdef IR_PIN_TOGGLE
    Pwm_IR_Interrupt_StartEx(IR_Pin_Toggle);
    Pwm_IR_Interrupt_Disable();
    IR_Led_Write(LOGIC_HIGH);
#endif /* IR_PIN_TOGGLE */
    bitsTransmitted = 0;
}

/*****************************************************************************
* Function Name: IR_Start()
******************************************************************************
* Summary:
* This function is used to start IR transmission 
*
* Parameters:
* report - IR Report containing command to be processed 
*
* Return:
* None
*
* Theory:
* Starts the PWM and required clocks for the the IR transmission
*
* Side Effects:
* prevCommand, irData and bitsTransmitted is updated
* 
* Note:
* Application should wait for the previous operation to complete before 
* initiating the next IR transmission
*
*****************************************************************************/
void IR_Start(IR_Report *report)
{
    uint8 i;
    uint8 commandReverse;
    uint16 address;
    /* Check for IR command validity */
    if(((report->command != INVALID) || (report->isRepeatCode)) && (bitsTransmitted == 0))
    {
#ifdef IR_PIN_TOGGLE
        Pwm_IR_Interrupt_Enable();
        IR_Led_Write(LOGIC_HIGH);
#endif /* IR_PIN_TOGGLE */        
        Pwm_IR_Timer_Interrupt_Enable();
        Pwm_IR_Timer_WriteCounter(0);
        Pwm_IR_Timer_Clk_Start();
        Pwm_IR_Clk_Start();
        Pwm_IR_Timer_WriteCompare(IR_INITIAL_STOP_PULSE);
        /* Check for IR command repeat functionality */
        if(report->isRepeatCode)
        {
#ifdef NEC_PROTOCOL /* Enable this macro to use NEC protocol in IR module */
            Pwm_IR_Timer_WritePeriod(IR_INITIAL_STOP_REPEAT_PULSE);
            bitsTransmitted = REPEAT_BIT_LENGTH;
            irData = 0;
#else
            report->isRepeatCode = 0;
            report->command = prevCommand;
#endif /* NEC_PROTOCOL */
        }
        /*If IR repeat functionality code is not set, do normal IR transmission*/
        if(!report->isRepeatCode)
        {
            irData = 0;
            Pwm_IR_Timer_WritePeriod(IR_INITIAL_PULSE);
            bitsTransmitted = IRDATA_LENGTH;
            address = irAddress;
            commandReverse = ~(report->command);
#ifndef NEC_PROTOCOL /* Enable this macro to use NEC protocol in IR module */
            prevCommand = report->command;
#endif /* NEC_PROTOCOL */
            /* Code for place the LSB first */
            for(i=0;i<BIT_8_POSITION;i++)
            {
                irData = irData<<BIT_1_POSITION;
                irData |=   (((address & (BIT_0_MASK | BIT_8_MASK))<<BIT_16_POSITION) | \
                                    ((report->command & BIT_0_MASK)<<BIT_8_POSITION) | \
                                                       (commandReverse & BIT_0_MASK));
                address = (address>>BIT_1_POSITION);
                commandReverse = (commandReverse>>BIT_1_POSITION);
                report->command = (report->command>>BIT_1_POSITION);
            }
        }
        Pwm_IR_WriteCounter(0);
        Pwm_IR_Start();
        Pwm_IR_Timer_Start();
    }
}

/*****************************************************************************
* Function Name: IR_Complete()
******************************************************************************
* Summary:
* This function is used to check IR data transmission status
*
* Parameters:
* None
*
* Return:
* Returns true if all bits transmitted successfully.
*
* Theory:
* Checks the number of bits transmitted
*
* Side Effects:
* None
* 
* Note:
*
*****************************************************************************/
bool IR_Complete(void)
{
    return (bitsTransmitted == 0);
}

/*****************************************************************************
* Function Name: IR_Stop()
******************************************************************************
* Summary:
* This function is used to stop IR Module functionality
*
* Parameters:
* None
*
* Return:
* None
*
* Theory:
* Stops the PWM and the clocks
*
* Side Effects:
* IR Receiver should have the capability to detect the  that the IR transmission
* has stopped. Otherwise it may cause problem at the receiver
* 
* Note:
* None
*****************************************************************************/
void IR_Stop(void)
{
#ifdef IR_PIN_TOGGLE
    Pwm_IR_Interrupt_Disable();
    Pwm_IR_Interrupt_ClearPending();
#endif /* IR_PIN_TOGGLE */
    Pwm_IR_Stop();
#ifdef IR_PIN_TOGGLE /* Alternate implementation is used UDB based PWM to toggle IR pin to process commands */
    IR_Led_Write(LOGIC_HIGH);
#endif /* IR_PIN_TOGGLE */

    Pwm_IR_Timer_Interrupt_Disable();
    Pwm_IR_Timer_Interrupt_ClearPending();
    Pwm_IR_Timer_Stop();
    Pwm_IR_Stop();
    Pwm_IR_Timer_Clk_Stop();
    Pwm_IR_Clk_Stop();
    bitsTransmitted = 0;
}

#endif /* DISABLE_IR */
